//线段树-扶苏的问题?
#include <iostream>
using namespace std;
#define lc p << 1
#define rc p << 1 | 1
const int N = 1e6 + 10;
typedef long long ll;
ll a[N];

struct tree
{
    int l, r;
    ll max;
    ll update, add; //懒标记
    bool st; //是否发生了重置, 0表示没有发生重置, 1表示发生了重置
}tr[N << 2];

void pushup(int p)
{
    tr[p].max = max(tr[lc].max, tr[rc].max);
}

void build(int p, int l, int r)
{
    tr[p] = {l, r, a[l], 0, 0, 0}; //update初始值给什么都不对, 因此需要额外的st标记是否发生了重置
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(lc, l, mid); build(rc, mid+1, r);
    pushup(p);
}

//必须先执行重置操作, 再执行加法操作
void lazy(int p, ll update, ll add, bool st)
{
    if(st) //发生了重置操作
    {
        tr[p].max = update;
        tr[p].update = update;
        tr[p].add = 0;
        tr[p].st = st;
    }
    tr[p].max += add;
    tr[p].add += add;
}

void pushdown(int p)
{
    lazy(lc, tr[p].update, tr[p].add, tr[p].st);
    lazy(rc, tr[p].update, tr[p].add, tr[p].st);
    tr[p].add = tr[p].update = tr[p].st = 0;
}

//多添加参数来说明是重置还是加法
void modify(int p, int x, int y, ll update, ll add, bool st)
{
    int l = tr[p].l, r = tr[p].r;
    if(x <= l && r <= y)
    {
        lazy(p, update, add, st);
        return;
    }
    pushdown(p);
    int mid = (l + r) >> 1;
    if(x <= mid) modify(lc, x, y, update, add, st);
    if(y > mid) modify(rc, x, y, update, add, st);
    pushup(p);
}

ll query(int p, int x, int y)
{
    int l = tr[p].l, r = tr[p].r;
    if(x <= l && r <= y) return tr[p].max;
    pushdown(p);
    int mid = (l + r) >> 1;
    ll ret = -1e18;
    if(x <= mid) ret = max(ret, query(lc, x, y));
    if(y > mid) ret = max(ret, query(rc, x, y));
    return ret;
}

int main()
{
    int n, q; cin >> n >> q;
    // for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    build(1, 1, n);
    while(q--)
    {
        int op, l, r, x; 
        //cin >> op >> l >> r;
        scanf("%d%d%d", &op, &l, &r);
        if(op == 1)
        {
            // cin >> x;
            scanf("%d", &x);
            modify(1, l, r, x, 0, 1);
        }
        else if(op == 2)
        {
            // cin >> x;
            scanf("%d", &x);
            modify(1, l, r, 0, x, 0);
        }
        else
        {
            // cout << query(1, l, r) << endl;
            printf("%ld\n", query(1, l, r));
        }
    }
    return 0;
}